class ParentClass<SomeType> {
  public function new() {}
  public function doSomething(input : Array<SomeType>) : Void {
    for (i in input)
    {
      action(i);
    }
  }

  function action(s : SomeType) : Void
  {
#if flash
    trace(s);
#elseif neko
    neko.Lib.print(s);
#end
  }
}

class ChildClass1 extends ParentClass<String> {
  public function new() {super();}
  override function action(s : String) : Void
  {
#if flash
    trace(s);
    trace(s);
#elseif neko
    neko.Lib.print(s);
    neko.Lib.print(s);
#end
  }
}

class ChildClass2 extends ParentClass<Int> {
  public function new() {super();}
  override function action(s : Int) : Void
  {
#if flash
    trace(Std.string(s));
    trace(Std.string(s));
    trace(Std.string(s));
#elseif neko
    neko.Lib.print(Std.string(s));
    neko.Lib.print(Std.string(s));
    neko.Lib.print(Std.string(s));
#end
  }
}

class Parent<SomeType> {
  var p : SomeType;
  public function new(p : SomeType) {
    this.p = p;
  }

  public function add(p : SomeType, q : SomeType) : SomeType {
    return p + q;
  }
}

class Child1 extends Parent<Int> {
  public function new(p : Int) {super(p);}
  public function print_p() : Void {
#if flash
    trace(Std.string(p));
#elseif neko
    neko.Lib.print(Std.string(p));
#end
  }
}

class Child2 extends Parent<String> {
  public function new(p : String) {super(p);}
  public function print_p() : Void {
#if flash
    trace(p);
#elseif neko
    neko.Lib.print(p);
#end
  }
}

class Test_template {
  static function main() {
    var child1 = new ChildClass1();
    child1.doSomething(["1","2","3"]); // no problem :)

    var child2 = new ChildClass2();
    child2.doSomething([1,2,3]); // no problem here either :)

    // Every time this code below is compiled an angel dies...
    // A hypothetical fix is by 'overriding' add or
    // explicitly casting somehow to the desired output type
    // of add.

    // Or this corner case could be fixed by Monsieur Canasse.
    var c1 = new Child1(5);
    c1.print_p();
    trace(Std.string(c1.add(1,2)));

    var c2 = new Child2("5");
    c2.print_p();
    trace(c2.add("1","2"));

    var p1 = new Parent<Int>(1);
    var p2 = new Parent<String>("String");
    trace(Std.string(p1.add(1,2)));
    trace(p2.add("1","2"));    
  }
}



